/*
 * PostgisConnection.java
 * 
 * Created by demory on Jun 20, 2009, 3:49:13 PM
 * 
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.fpdev.core.data;

//import com.vividsolutions.jts.geom.GeometryFactory;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.fpdev.core.FPEngine;
import org.fpdev.core.DataPackage;
import org.fpdev.core.basenet.BLink;
import org.fpdev.core.basenet.BLinkStreet;
import org.fpdev.core.basenet.BNode;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureStore;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Transaction;
import org.geotools.data.postgis.PostgisDataStore;
import org.geotools.data.postgis.PostgisDataStoreFactory;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.filter.Filter;

/**
 *
 * @author demory
 */
public class PostgisConnection extends QueryableDataConnection { //implements DataConnection {

  //private FPEngine engine_;

  private DataStore pgDataStore_;

  //private PostgisDataStore store_;
  //private PostgisFeatureStore nodeFeatStore_, linkFeatStore_;

  //private GeometryFactory gf_;
  //private FilterFactory2 ff_;

  public PostgisConnection(FPEngine engine, DataPackage dp) { //, DataPackage pkg, String user, String pw) {
    super(engine);
    //engine_ = engine;
    try {

      //gf_ = new GeometryFactory();
      //ff_ = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints());

      // create the datastore
      java.util.Map params = new java.util.HashMap();
      params.put(PostgisDataStoreFactory.DBTYPE.key, "postgis");
      params.put(PostgisDataStoreFactory.HOST.key, "localhost");
      params.put(PostgisDataStoreFactory.PORT.key, new Integer(5432));
      params.put(PostgisDataStoreFactory.SCHEMA.key, "public");

      String dbName = "fp_" + dp.getName();
      String username = dp.getProperty("pgUsername");
      String password = dp.getProperty("pgPassword");
      /*System.out.println("dbname="+dbName);
      System.out.println("username="+username);
      System.out.println("password="+password);*/
      params.put(PostgisDataStoreFactory.DATABASE.key, dbName);
      params.put(PostgisDataStoreFactory.USER.key, username);
      params.put(PostgisDataStoreFactory.PASSWD.key, password);

      pgDataStore_ = (DataStore) DataStoreFinder.getDataStore(params);

      nodeFeatStore_ = (FeatureStore) pgDataStore_.getFeatureSource("nodes");
      linkFeatStore_ = (FeatureStore) pgDataStore_.getFeatureSource("links");
      
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }

  public String getGeomFieldName() {
    return "geom";
  }

  //** DB modification methods  **//

  public void createNode(BNode node) {
    try {
      nodeFeatStore_.setTransaction(Transaction.AUTO_COMMIT);
      engine_.getDataPackage().getFeatureIO().writeNode(node, nodeFeatStore_);//writer);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  public void deleteNode(BNode node) {
    try {
      nodeFeatStore_.setTransaction(Transaction.AUTO_COMMIT);
      nodeFeatStore_.removeFeatures(ff2_.equals(ff2_.property("fpid"), ff2_.literal(node.getID())));
    } catch (Exception ex) {
      ex.printStackTrace();
    }

  }

  public void createLink(BLink link) {
    try {
      linkFeatStore_.setTransaction(Transaction.AUTO_COMMIT);
      engine_.getDataPackage().getFeatureIO().writeLink(link, linkFeatStore_);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  public void deleteLink(BLink link) {
    try {
      linkFeatStore_.setTransaction(Transaction.AUTO_COMMIT);
      linkFeatStore_.removeFeatures(ff2_.equals(ff2_.property("fpid"), ff2_.literal(link.getID())));
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }

  public void modifyNode(BNode node, NodeAttr[] attrs) {
    modifyNodes(Collections.singleton(node), attrs);
  }

  public void modifyNodes(Collection<BNode> nodes, NodeAttr[] attrs) {

    AttributeDescriptor attrDescs[] = new AttributeDescriptor[attrs.length];
    Object[][] values = new Object[nodes.size()][attrs.length];

    SimpleFeatureType nodeFT = engine_.getDataPackage().getFeatureIO().getNodeFeatureType();

    int ni;
    for(int ai=0; ai<attrs.length; ai++) {
      switch(attrs[ai]) {
        case SHAPE:
          attrDescs[ai] = nodeFT.getDescriptor("geom");
          ni = 0;
          for(BNode node : nodes) {
            values[ni][ai] = engine_.getDataPackage().getFeatureIO().constructNodeGeometry(node);
            ni++;
          }
          break;
        case SCEN:
          attrDescs[ai] = nodeFT.getDescriptor("scen");
          ni = 0;
          for(BNode node : nodes) {
            values[ni][ai] = node.getScenario().getID();
            ni++;
          }
          break;
      }
    }
    
    try {
      Transaction trans = new DefaultTransaction("handle");
      nodeFeatStore_.setTransaction(trans); //Transaction.AUTO_COMMIT);

      ni = 0;
      for(BNode node : nodes) {
        Filter filter = ff2_.equals(ff2_.property("fpid"), ff2_.literal(node.getID()));
        nodeFeatStore_.modifyFeatures(attrDescs, values[ni], filter);
        ni++;
      }
      trans.commit();
      trans.close();
    } catch (IOException ex) {
      Logger.getLogger(PostgisConnection.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

  public void modifyLink(BLink link, LinkAttr[] attrs) {
    modifyLinks(Collections.singleton(link), attrs);
  }

  public void modifyLinks(Collection<BLink> links, LinkAttr[] attrs) {
    AttributeDescriptor attrDescs[] = new AttributeDescriptor[attrs.length];
    Object[][] values = new Object[links.size()][attrs.length];

    SimpleFeatureType linkFT = engine_.getDataPackage().getFeatureIO().getLinkFeatureType();

    int li;

    for(int ai=0; ai<attrs.length; ai++) {
      switch(attrs[ai]) {
        case SHAPE:
          attrDescs[ai] = linkFT.getDescriptor("geom");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = engine_.getDataPackage().getFeatureIO().constructLinkGeometry(link);
            li++;
          }
          break;
        case SCEN:
          attrDescs[ai] = linkFT.getDescriptor("scen");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = link.getScenario().getID();
            li++;
          }
          break;
        case NAME:
          attrDescs[ai] = linkFT.getDescriptor("name");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = link.getDisplayName();
            li++;
          }
          break;
        case TYPE:
          attrDescs[ai] = linkFT.getDescriptor("exttype");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = link.getExtendedType();
            li++;
          }
          break;
        case FNODE:
          attrDescs[ai] = linkFT.getDescriptor("fnode");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = link.getFNodeID();
            li++;
          }
          break;
        case TNODE:
          attrDescs[ai] = linkFT.getDescriptor("tnode");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = link.getTNodeID();
            li++;
          }
          break;
        case LEN:
          attrDescs[ai] = linkFT.getDescriptor("len");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = link.getLengthFeet();
            li++;
          }
          break;
        case UPFW:
          attrDescs[ai] = linkFT.getDescriptor("upfw");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = link.getUpSlopeFW();
            li++;
          }
          break;
        case UPBW:
          attrDescs[ai] = linkFT.getDescriptor("upbw");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = link.getUpSlopeBW();
            li++;
          }
          break;
        case ELEVARR:
          attrDescs[ai] = linkFT.getDescriptor("elevarr");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = engine_.getDataPackage().getFeatureIO().getElevationArray(link);
            li++;
          }
          break;
        case TORARR:
          attrDescs[ai] = linkFT.getDescriptor("torarr");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = engine_.getDataPackage().getFeatureIO().getTypeOverrideArray(link);
            li++;
          }
          break;
        case FADDRL:
          attrDescs[ai] = linkFT.getDescriptor("faddrl");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = (link.getClassType() == BLink.CLASS_STREET) ?
              ((BLinkStreet) link).getFAddrL() : 0;
            li++;
          }
          break;
        case TADDRL:
          attrDescs[ai] = linkFT.getDescriptor("taddrl");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = (link.getClassType() == BLink.CLASS_STREET) ?
              ((BLinkStreet) link).getTAddrL() : 0;
            li++;
          }
          break;
        case FADDRR:
          attrDescs[ai] = linkFT.getDescriptor("faddrr");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = (link.getClassType() == BLink.CLASS_STREET) ?
              ((BLinkStreet) link).getFAddrR() : 0;
            li++;
          }
          break;
        case TADDRR:
          attrDescs[ai] = linkFT.getDescriptor("taddrr");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = (link.getClassType() == BLink.CLASS_STREET) ?
              ((BLinkStreet) link).getTAddrR() : 0;
            li++;
          }
          break;
        case ZIPR:
          attrDescs[ai] = linkFT.getDescriptor("zipr");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = (link.getClassType() == BLink.CLASS_STREET) ?
              ((BLinkStreet) link).getZipR() : 0;
            li++;
          }
          break;
        case ZIPL:
          attrDescs[ai] = linkFT.getDescriptor("zipl");
          li = 0;
          for(BLink link : links) {
            values[li][ai] = (link.getClassType() == BLink.CLASS_STREET) ?
              ((BLinkStreet) link).getZipL() : 0;
            li++;
          }
          break;
      }
    }

    try {
      Transaction trans = new DefaultTransaction("handle");
      linkFeatStore_.setTransaction(trans); //Transaction.AUTO_COMMIT);

      li = 0;
      for(BLink link : links) {
        Filter filter = ff2_.equals(ff2_.property("fpid"), ff2_.literal(link.getID()));
        linkFeatStore_.modifyFeatures(attrDescs, values[li], filter);
        li++;
      }
      trans.commit();
      trans.close();

    } catch (IOException ex) {
      Logger.getLogger(PostgisConnection.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

  public void close() {
    System.out.println("closing postgis connection");
    pgDataStore_.dispose();
  }

}
